//
//  Scanner+UIColor.swift
//  MKCodableTests
//
//  Created by 苏尚进 on 2020/5/11.
//  Copyright © 2020 moon. All rights reserved.
//

import Foundation
import UIKit
import MKCodable

extension Scanner {
    var mk:NameSpace<Scanner> { return NameSpace<Scanner>(self) }
}
extension UIColor {
    var mk:NameSpace<UIColor> { return NameSpace<UIColor>(self) }
}

extension NameSpace where T:Scanner {
    public enum ColorName : String , Codable{
        case red
        case green
        case blue
        case black
        case gray
        case white
        case purple
        public var color:UIColor {
            switch self {
            case .red:
                return .red
            case .green:
                return .green
            case .blue:
                return .blue
            case .black:
                return .black
            case .gray:
                return .gray
            case .white:
                return .white
            case .purple:
                return .purple
            }
        }
        @inlinable public static var allValues:[ColorName]{ return [red,green,blue,black,gray,white,purple] }
    }
    
    public enum ColorRepresentation {
        case name//颜色的名称，比如red, blue, brown, lightseagreen等，不区分大小写
        ///
        ///color:red;    /* 红色 */
        ///color:black;  /* 黑色 */
        ///color:gray;   /* 灰色 */
        ///color:white;  /* 白色 */
        ///color:purple; /* 紫色 */
        
        case hex//十六进制符号 #RRGGBB 和 #RGB（比如 #ff0000）。"#" 后跟 6 位或者 3 位十六进制字符（0-9, A-F）
//        #f03
//        #F03
//        #ff0033
//        #FF0033

        case rgb//红-绿-蓝（red-green-blue (RGB)）
//        规定颜色值为 rgb 代码的颜色，函数格式为 rgb(R,G,B)，取值可以是 0-255 的整数或百分比。
//        rgb(255,0,51)
//        rgb(255, 0, 51)
//        rgb(100%,0%,20%)
//        rgb(100%, 0%, 20%)
        
        case rgba//红-绿-蓝-阿尔法（RGBa）
//        RGBa 扩展了 RGB 颜色模式，它包含了阿尔法通道，允许设定一个颜色的透明度。a 表示透明度：0=透明；1=不透明。
//        rgba(255,0,0,0.1)    /* 10% 不透明 */
//        rgba(255,0,0,0.4)    /* 40% 不透明 */
//        rgba(255,0,0,0.7)    /* 70% 不透明 */
//        rgba(255,0,0,  1)    /* 不透明，即红色 */
        
        case hsl//色相-饱和度-明度（Hue-saturation-lightness）
//        色相（Hue）表示色环（即代表彩虹的一个圆环）的一个角度。
//        饱和度和明度由百分数来表示。
//        100% 是满饱和度，而 0% 是一种灰度。
//        100% 明度是白色， 0% 明度是黑色，而 50% 明度是"一般的"。
        
//        hsl(120,100%,25%)    /* 深绿色 */
//        hsl(120,100%,50%)    /* 绿色 */
//        hsl(120,100%,75%)    /* 浅绿色 */
        
        case hsla//色相-饱和度-明度-阿尔法（HSLa）
//        HSLa 扩展自 HSL 颜色模式，包含了阿尔法通道，可以规定一个颜色的透明度。 a 表示透明度：0=透明；1=不透明。
//        hsla(240,100%,50%,0.05)   /* 5% 不透明 */
//        hsla(240,100%,50%, 0.4)   /* 40% 不透明 */
//        hsla(240,100%,50%, 0.7)   /* 70% 不透明 */
//        hsla(240,100%,50%,   1)   /* 完全不透明 */
    }
    public struct ScanError:Error {
        let reason:String
        public init(_ reason:String) {
            self.reason = reason
        }
    }
    public func scanColor() -> UIColor? {
        var color:UIColor?
        let representation:[ColorRepresentation] = [.hex,.rgba,.rgb,.hsla,.hsl,.name]
        let cachedScanLocation = self.value.scanLocation
        for representation in representation {
            self.value.scanLocation = cachedScanLocation
            if let t = try? tryScanColor(representation: representation) {
                color = t
                break
            }
        }
        return color
    }
    
    public func scanColor(representation: ColorRepresentation = .name) -> UIColor? {
        do {
            let color = try tryScanColor(representation: representation)
            return color
        } catch {
            print(error)
        }
        return nil
    }
    public func tryScanColor(representation: ColorRepresentation = .name) throws -> UIColor {
        var color:UIColor
        switch representation {
        case .name:
            color = try scanColorWithName()
        case .hex:
            color = try scanColorWithHex()
            break
        case .rgb:
            color = try scanColorWithRGB(includeAplha: false)
            break
        case .rgba:
            color = try scanColorWithRGB(includeAplha: true)
            break
        case .hsl:
            color = try scanColorWithHSL(includeAplha: false)
            break
        case .hsla:
            color = try scanColorWithHSL(includeAplha: true)
            break
        }
        return color
    }
    internal func scanColorWithName() throws -> UIColor {
        var color:UIColor?
        for name in ColorName.allValues {
             if #available(iOS 13.0, *) {
                if let _ = self.value.scanString(name.rawValue) {
                    color = name.color
                    break
                }
            } else {
                if self.value.scanString(name.rawValue, into: nil) {
                    color = name.color
                    break
                }
            }
        }
        if color == nil {
            throw ScanError("Scan color string \(self.value.string) fail,it should be in [\(ColorName.allValues.map{$0.rawValue}.joined(separator: ","))]")
        }
        return color!
    }
    internal func scanColorWithHex() throws -> UIColor  {
        var color:UIColor
        var hexValue:UInt64?
        if #available(iOS 13.0, *) {
            if let _ = self.value.scanString("#") {
                var v : UInt64 = 0
                if self.value.scanHexInt64(&v) {
                    hexValue = v
                }
            }
        } else {
            if self.value.scanString("#", into: nil) {
                var v : UInt64 = 0
                if self.value.scanHexInt64(&v) {
                    hexValue = v
                }
            }
        }
        if let hex = hexValue {
            switch self.value.string.count {
            case 4://#f03
                let redValue = (CGFloat((hex & 0xF00) >> 4)+CGFloat((hex & 0xF00) >> 8))/255.0
                let greenValue = (CGFloat((hex & 0xF0))+CGFloat((hex & 0xF0) >> 4))/255.0
                let blueValue = (CGFloat((hex & 0xF) << 4)+CGFloat(hex & 0xF))/255.0
                let alpha:CGFloat = 1.0
                color = UIColor(red: redValue, green: greenValue, blue: blueValue, alpha: alpha)
            case 7://#ff0033
                let redValue = CGFloat((hex & 0xFF0000) >> 16)/255.0
                let greenValue = CGFloat((hex & 0xFF00) >> 8)/255.0
                let blueValue = CGFloat(hex & 0xFF)/255.0
                let alpha:CGFloat = 1.0
                color = UIColor(red: redValue, green: greenValue, blue: blueValue, alpha: alpha)
            default:
                throw ScanError("Scan color string \(self.value.string) fail,it should be like #RRGGBB or #RGB . eg #ff0033 or #f03")
            }
        } else {
            throw ScanError("Scan color string \(self.value.string) fail,it should be like #RRGGBB or #RGB . eg #ff0033 or #f03")
        }
        return color
    }
    internal func scanColorComponent(prefix:String) throws -> [CGFloat] {
        var components:[CGFloat] = []
        self.value.caseSensitive = true
        self.value.charactersToBeSkipped = [" "]
        if #available(iOS 13.0, *) {
            guard let _ = self.value.scanString(prefix) else {
                throw ScanError("Scan color string \(self.value.string) fail,did no have prefix :\(prefix)")
            }
        } else {
            guard self.value.scanString(prefix, into: nil) else {
                throw ScanError("Scan color string \(self.value.string) fail, did no have prefix :\(prefix)")
            }
        }
        for i in 0...3 {
            var u8:UInt64?
            if #available(iOS 13.0, *) {
                if i == 3 {
                    //alpha
                    if let a = self.value.scanFloat() {
                        if a >= 0.0 && a <= 1.0 {
                            components.append(CGFloat(a))
                        }else{
                            throw ScanError("Scan color string \(self.value.string) fail, have wrong alpha:\(a.description) , it should be in [0.0,1.0]")
                        }
                    }
                } else if let t = self.value.scanUInt64() {
                    u8 = t
                    if let _ = self.value.scanString("%") {
                        let f = CGFloat(u8!)/100.0
                        guard f >= 0.0 && f <= 1.0 else {
                            throw ScanError("Scan color string \(self.value.string) fail, have wrong color component:\(u8!.description)% , it should be in [0,100]%")
                        }
                        components.append(f)
                        let _ = self.value.scanString(",")
                    } else {
                        let f = CGFloat(u8!)/255.0
                        guard f >= 0.0 && f <= 1.0 else {
                            throw ScanError("Scan color string \(self.value.string) fail, have wrong color component:\(u8!.description) , it should be in [0,255]")
                        }
                        components.append(f)
                        let _ = self.value.scanString(",")
                    }
                } else {
                    throw ScanError("Scan color string \(self.value.string) fail, color component should be [0,1]")
                }
            } else {
                if i == 3 {
                    //alpha
                    var a : Float = 0.0
                    if self.value.scanFloat(&a) {
                        if a >= 0.0 && a <= 1.0 {
                            components.append(CGFloat(a))
                        }else{
                            throw ScanError("Scan color string \(self.value.string) fail, have wrong alpha:\(a.description) , it should be in [0.0,1.0]")
                        }
                    }
                } else {
                    var t:UInt64 = 0
                    if self.value.scanUnsignedLongLong(&t) {
                        u8 = t
                        if self.value.scanString("%", into: nil) {
                            let f = CGFloat(u8!)/100.0
                            guard f >= 0.0 && f <= 1.0 else {
                                throw ScanError("Scan color string \(self.value.string) fail, have wrong color component:\(u8!.description)% , it should be in [0,100]%")
                            }
                            components.append(f)
                            self.value.scanString(",", into: nil)
                        } else {
                            let f = CGFloat(u8!)/255.0
                            guard f >= 0.0 && f <= 1.0 else {
                                throw ScanError("Scan color string \(self.value.string) fail, have wrong color component:\(u8!.description) , it should be in [0,255]")
                            }
                            components.append(f)
                            self.value.scanString(",", into: nil)
                        }
                    } else {
                        throw ScanError("Scan color string \(self.value.string) fail, color component should be [0,100]% or [0,255]")
                    }
                }
            }
        }
        if #available(iOS 13.0, *) {
            guard let _ = self.value.scanString(")") else {
                throw ScanError("Scan color string \(self.value.string) fail, do not end with \")\"")
            }
        }else{
            guard self.value.scanString(")", into: nil) else {
                throw ScanError("Scan color string \(self.value.string) fail, do not end with \")\"")
            }
        }
        guard self.value.isAtEnd else {
            throw ScanError("Scan color string \(self.value.string) fail, do not end with \")\"")
        }
        return components
    }
    internal func scanColorWithRGB(includeAplha:Bool) throws -> UIColor {
        //        rgb(255,0,51)
        //        rgb(255, 0, 51)
        //        rgb(100%,0%,20%)
        //        rgb(100%, 0%, 20%)

        //        rgba(255,0,0,0.1)    /* 10% 不透明 */
        //        rgba(255,0,0,0.4)    /* 40% 不透明 */
        //        rgba(255,0,0,0.7)    /* 70% 不透明 */
        //        rgba(255,0,0,  1)    /* 不透明，即红色 */
        var color:UIColor
        self.value.caseSensitive = true
        self.value.charactersToBeSkipped = [" "]
        let prefix = includeAplha ? "rgba(" : "rgb("
        let list = try scanColorComponent(prefix: prefix)
        if includeAplha {
            guard list.count == 4 else {
                throw ScanError("Scan color string \(self.value.string) fail, need 4 color components")
            }
        } else {
            guard list.count == 3 else {
                throw ScanError("Scan color string \(self.value.string) fail, need 3 color components")
            }
        }
        switch list.count {
        case 3:
            color = UIColor(red: list[0], green: list[1], blue: list[2], alpha: 1.0)
        case 4:
            color = UIColor(red: list[0], green: list[1], blue: list[2], alpha: list[3])
        default:
            throw ScanError("Scan color string \(self.value.string) fail, have no enougth color components \(list.map{$0.description}.joined(separator: ","))")
        }
        return color
    }
    internal func scanColorWithHSL(includeAplha:Bool) throws -> UIColor {
        //        色相（Hue）表示色环（即代表彩虹的一个圆环）的一个角度。
        //        饱和度和明度由百分数来表示。
        //        100% 是满饱和度，而 0% 是一种灰度。
        //        100% 明度是白色， 0% 明度是黑色，而 50% 明度是"一般的"。
                
        //        hsl(120,100%,25%)    /* 深绿色 */
        //        hsl(120,100%,50%)    /* 绿色 */
        //        hsl(120,100%,75%)    /* 浅绿色 */

        //        HSLa 扩展自 HSL 颜色模式，包含了阿尔法通道，可以规定一个颜色的透明度。 a 表示透明度：0=透明；1=不透明。
        //        hsla(240,100%,50%,0.05)   /* 5% 不透明 */
        //        hsla(240,100%,50%, 0.4)   /* 40% 不透明 */
        //        hsla(240,100%,50%, 0.7)   /* 70% 不透明 */
        //        hsla(240,100%,50%,   1)   /* 完全不透明 */
        var color:UIColor
        self.value.caseSensitive = true
        self.value.charactersToBeSkipped = [" "]
        let prefix = includeAplha ? "hsla(" : "hsl("
        let list = try scanColorComponent(prefix: prefix)
        if includeAplha {
            guard list.count == 4 else {
                throw ScanError("Scan color string \(self.value.string) fail, need 4 color components")
            }
        } else {
            guard list.count == 3 else {
                throw ScanError("Scan color string \(self.value.string) fail, need 3 color components")
            }
        }
        switch list.count {
        case 3:
            color = UIColor(hue: list[0], saturation: list[1], brightness: list[2], alpha: 1.0)
        case 4:
            color = UIColor(hue: list[0], saturation: list[1], brightness: list[2], alpha: list[3])
        default:
            throw ScanError("Scan color string \(self.value.string) fail, have no enougth color components \(list.map{$0.description}.joined(separator: ","))")
        }
        return color
    }
}

extension NameSpace where T:UIColor {
    public var rgba:(Float,Float,Float,Float) {//返回(r,g,b,a)
        var r:CGFloat = 0.0
        var g:CGFloat = 0.0
        var b:CGFloat = 0.0
        var a:CGFloat = 0.0
        self.value.getRed(&r, green: &g, blue: &b, alpha: &a)
        return (Float(r),Float(g),Float(b),Float(a))
    }
    public var hsba:(Float,Float,Float,Float) {
        var h:CGFloat = 0.0
        var s:CGFloat = 0.0
        var b:CGFloat = 0.0
        var a:CGFloat = 0.0
        self.value.getHue(&h, saturation: &s, brightness: &b, alpha: &a)
        return (Float(h),Float(s),Float(b),Float(a))
    }
}

